#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <random>
#include <chrono>
#include <atomic>

std::mutex cout_mutex;
std::atomic<bool> running(true);

struct Packet {
    int seq_num;
    std::string data;
};

class Sender {
private:
    std::queue<Packet> packets;
    std::mutex mtx;
    std::atomic<bool> waiting_for_ack{false};
    std::atomic<int> current_seq{0};
    int retry_count = 0;
    const int max_retries = 3;
    double loss_probability;

public:
    Sender(double loss_prob) : loss_probability(loss_prob) {
        for (int i = 0; i < 10; ++i) {
            packets.push({i, "Packet " + std::to_string(i)});
        }
    }

    void send(Packet& packet, Receiver& receiver);
    void receive_ack(int ack_seq);
    void run(Receiver& receiver);
};

class Receiver {
private:
    std::atomic<int> expected_seq{0};
    double loss_probability;

public:
    Receiver(double loss_prob) : loss_probability(loss_prob) {}

    void receive(Packet& packet, Sender& sender);
    void run(Sender& sender);
};

void Sender::send(Packet& packet, Receiver& receiver) {
    std::lock_guard<std::mutex> lock(mtx);
    std::random_device rd;
    std::mt19937 gen(rd());
    std::bernoulli_distribution dist(loss_probability);

    if (!dist(gen)) {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Sender: Sending packet " << packet.seq_num << std::endl;
        }
        receiver.receive(packet, *this);
    } else {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Sender: Packet " << packet.seq_num << " lost!" << std::endl;
        }
    }
    waiting_for_ack = true;
}

void Sender::receive_ack(int ack_seq) {
    if (ack_seq == current_seq) {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Sender: Received ACK for packet " << ack_seq << std::endl;
        }
        waiting_for_ack = false;
        current_seq++;
        retry_count = 0;
    } else {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Sender: Received wrong ACK " << ack_seq << ", expecting " << current_seq << std::endl;
        }
    }
}

void Sender::run(Receiver& receiver) {
    while (running && !packets.empty()) {
        if (!waiting_for_ack) {
            Packet packet = packets.front();
            if (packet.seq_num == current_seq) {
                send(packet, receiver);
                retry_count++;
            } else {
                packets.pop();
            }
        } else {
            if (retry_count >= max_retries) {
                {
                    std::lock_guard<std::mutex> lock_cout(cout_mutex);
                    std::cout << "Sender: Max retries reached for packet " << current_seq << ", moving to next packet" << std::endl;
                }
                waiting_for_ack = false;
                current_seq++;
                retry_count = 0;
                packets.pop();
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

void Receiver::receive(Packet& packet, Sender& sender) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::bernoulli_distribution dist(loss_probability);

    if (packet.seq_num == expected_seq) {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Receiver: Received packet " << packet.seq_num << ": " << packet.data << std::endl;
        }
        expected_seq++;

        if (!dist(gen)) {
            {
                std::lock_guard<std::mutex> lock_cout(cout_mutex);
                std::cout << "Receiver: Sending ACK " << packet.seq_num << std::endl;
            }
            sender.receive_ack(packet.seq_num);
        } else {
            {
                std::lock_guard<std::mutex> lock_cout(cout_mutex);
                std::cout << "Receiver: ACK " << packet.seq_num << " lost!" << std::endl;
            }
        }
    } else {
        {
            std::lock_guard<std::mutex> lock_cout(cout_mutex);
            std::cout << "Receiver: Discarding out-of-order packet " << packet.seq_num << std::endl;
        }
    }
}

void Receiver::run(Sender& sender) {
    while (running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    const double loss_probability = 0.3;

    Sender sender(loss_probability);
    Receiver receiver(loss_probability);

    std::thread sender_thread(&Sender::run, &sender, std::ref(receiver));
    std::thread receiver_thread(&Receiver::run, &receiver, std::ref(sender));

    std::this_thread::sleep_for(std::chrono::seconds(10));
    running = false;

    sender_thread.join();
    receiver_thread.join();

    return 0;
}